using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 扩展Transform的查找方法
/// </summary>
public static class MonoObjExtender
{
    #region Transform的扩展方法
    /// <summary>
    /// 获得一个物体（或者其组件），可采用BFS（或DFS）的查找方法
    /// </summary>
    /// <param name="useBFS">适用于层级较低的内容，避免较深查找</param>
    public static T GetChildByName<T>(this Transform parent, string childObjName, bool useBFS = true)
    {
        Transform childGot;
        if (useBFS)
        {
            childGot = GetChildByNameInBFS(parent, childObjName);
        }
        else
        {
            childGot = GetChildByNameInDFS(parent, childObjName);
        }

        if (childGot == null)
            //Not found
            return default(T);
        else
            return childGot.GetComponent<T>();
    }

    /// <summary>
    /// 获得一个Transform，可选择BFS（默认广度优先搜索）或者DFS
    /// </summary>
    /// <param name="useBFS">适用于层级较低的内容，避免较深查找</param>
    public static Transform GetChildByName(this Transform parent, string childObjName, bool useBFS = true)
    {
        if (useBFS)
            return GetChildByNameInBFS(parent, childObjName);
        else
            return GetChildByNameInDFS(parent, childObjName);
    }

    /// <summary>
    /// 判断此物体是否是某个物体的子物体（层级关系限制为父子关系，而不会有爷孙关系）
    /// <para>Unity自带方法则会向上一直搜索</para>
    /// </summary>
    /// <param name="withoutFinding">不往上搜索</param>
    public static bool IsChildOf(this Transform obj, Transform objToCompare, bool withoutFinding = true)
    {
        if (withoutFinding)     //不用向上找的时候，直接判断
        {
            return obj.parent == objToCompare;
        }
        else                    //需要向上找的时候，使用自带方法
        {
            return obj.IsChildOf(objToCompare);
        }
    }

    /// <summary>
    /// 获得一级范围的子物体（只包括子代，不包括孙代）
    /// </summary>
    /// <returns>一级范围内的子物体</returns>
    public static GameObject[] GetChilds(this Transform objTrans)
    {
        int childCount = objTrans.childCount;
        GameObject[] result = new GameObject[childCount];

        for (int i = 0; i < childCount; i++)
        {
            result[i] = objTrans.GetChild(i).gameObject;
        }

        return result;

    }

    /// <summary>
    /// 获得一级范围的子物体（只包括子代，不包括孙代）
    /// </summary>
    /// <param name="namePrefix">名字前缀，可指定只获取符合某些名字前缀的物体</param>
    /// <returns>一级范围内的子物体</returns>
    public static GameObject[] GetChilds(this Transform objTrans,string namePrefix=null)
    {
        int childCount = objTrans.childCount;
        var result = new List<GameObject>();

        for (int i = 0; i < childCount; i++)
        {
            var obj = objTrans.GetChild(i).gameObject;
            if (!string.IsNullOrEmpty(namePrefix) && obj.name.StartsWith(namePrefix))
            {
                result.Add(obj);
            }
        }

        return result.ToArray();

    }

    /// <summary>
    /// 计算物体间的【平方距离】
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="objTarget"></param>
    /// <returns></returns>
    public static float SqrDistanceTo(this Transform obj, Transform objTarget)
    {
        return Vector3.SqrMagnitude(obj.position - objTarget.position);
    }

    /// <summary>
    /// 计算物体间的【平方距离】
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="objTarget"></param>
    /// <returns></returns>
    public static float SqrDistanceTo(this Transform obj, Vector3 v3Pos)
    {
        return Vector3.SqrMagnitude(obj.position - v3Pos);
    }

    /// <summary>
    /// DFS查找，获得Transform的子物体（及其后代）中第一个含有该名字的物体。适用于需要深度搜索的情况
    /// </summary>
    /// <param name="childObjName">子物体的名字</param>
    private static Transform GetChildByNameInDFS(this Transform parent, string childObjName)
    {
        //深度优先（DFS）找到该名字的Transform 
        //使用一个栈进行遍历 =_=
        System.Collections.Generic.Stack<Transform> stack =
            new System.Collections.Generic.Stack<Transform>();

        //--将parent入栈
        stack.Push(parent);

        Transform tmp;
        //--DFS开始
        while (stack.Count > 0)
        {
            tmp = stack.Pop();

            if (tmp.name.Equals(childObjName))
            {
                //Great, got it
                return (Transform)tmp;
            }

            //--push back the children
            for (int i = tmp.childCount - 1; i >= 0; i--)
            {
                stack.Push(tmp.GetChild(i));
            }
        }

        //Not found
        return null;
    }

    /// <summary>
    /// 采用广度优先的方式查找Transform的子物体（及其后代）中第一个含有该名字的物体。适用于无需太多深度搜索的情况；
    /// </summary>
    private static Transform GetChildByNameInBFS(this Transform parent, string childObjName)
    {
        //深度优先（BFS）找到该名字的Transform 
        //使用一个栈进行遍历 =_=
        System.Collections.Generic.Queue<Transform> stack =
            new System.Collections.Generic.Queue<Transform>();

        //--将parent入栈
        stack.Enqueue(parent);

        Transform tmp;
        //--BFS开始
        while (stack.Count > 0)
        {
            tmp = stack.Dequeue();

            if (tmp.name.Equals(childObjName))
            {
                //Great, got it
                return (Transform)tmp;
            }

            //--push back the children
            for (int i = 0; i < tmp.childCount; i++)
            {
                stack.Enqueue(tmp.GetChild(i));
            }
        }

        //Not found
        return null;
    }

    /// <summary>
    /// 设置物体Active与否
    /// </summary>
    public static void SetActive(this Transform trans, bool isOn)
    {
        trans.gameObject.SetActive(isOn);
    }

    /// <summary>
    /// 克隆此Transform对应的游戏物体，并获得游戏物体
    /// </summary>
    public static GameObject CloneObj(this Transform trans)
    {
        return GameObject.Instantiate(trans.gameObject);
    }

    /// <summary>
    /// 克隆此Transform对应的游戏物体，并获得克隆后的Transform
    /// </summary>
    public static Transform Clone(this Transform trans)
    {
        return GameObject.Instantiate(trans.gameObject).transform;
    }


    /// <summary>
    /// 判断某个Target位置是否在此物体之前
    /// </summary>
    [System.Obsolete("不要使用此方法，意义容易混淆")]
    public static bool IsPosForward(this Transform src, Transform target)
    {
        //--点乘，为正，则在前
        Vector3 srcV3 = src.forward;                        //向量：src的前面
        Vector3 deltaAn = target.position - src.position;//向量：src->target
        return Vector3.Dot(srcV3, deltaAn) > 0;
    }


    /// <summary>
    /// 判断当前物体是否在某个target物体之前
    /// </summary>
    public static bool IsInfrontOf(this Transform src, Transform target)
    {
        //--点乘，为正，则在前
        Vector3 srcV3 = target.forward;                        //向量：src的前面
        Vector3 deltaAn = src.position - target.position;//向量：src->target
        return Vector3.Dot(srcV3, deltaAn) > 0;
    }

    /// <summary>
    /// 物体是否在Target物体之上(直接判断y轴）
    /// </summary>
    /// <returns><c>true</c>, if position above was ised, <c>false</c> otherwise.</returns>
    /// <param name="src">Source.</param>
    /// <param name="target">Target.</param>
    /// <param name="deltaToTarget">还要比target高多少才算above</param>
    public static bool IsAbove(this Transform src, Transform target, float deltaToTarget = 0)
    {
        return src.position.y > target.position.y + deltaToTarget;
    }

    /// <summary>
    /// 使用射线判断，当前物体是否是在目标物体的上面
    /// </summary>
    /// <param name="src"></param>
    /// <param name="target"></param>
    /// <returns></returns>
    public static bool IsRayAbove(this Transform src, Transform target)
    {
        Ray ray = new Ray(src.position + Vector3.up, Vector3.down);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 2f)) //射线初始位置上提，所以射线距离需要延长
        {
            Debug.Log("ray hit " + hit.transform.name);
            if (hit.transform == target.transform)
            {
                return true;
            }
        }

        return false;
    }


    /// <summary>
    /// 使用射线判断，当前物体是否是在目标物体的上面
    /// </summary>
    /// <returns></returns>
    public static bool IsRayAbove(Vector3 pos, Transform target, float rayDistance)
    {
        Ray ray = new Ray(pos, Vector3.down);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, rayDistance)) //射线初始位置上提，所以射线距离需要延长
        {
            Debug.Log("ray hit " + hit.transform.name);
            if (hit.transform == target.transform)
            {
                return true;
            }
        }

        return false;
    }
    #endregion

    #region GameObject 的扩展方法



    #endregion
}
